package com.hsh.erp.service.impl;

import com.hsh.erp.constants.ProductConstants;
import com.hsh.erp.constants.RegexConstants;
import com.hsh.erp.constants.ResultConstants;
import com.hsh.erp.constants.SpecialSymbolConstants;
import com.hsh.erp.constants.filepath.ExpressPathConstants;
import com.hsh.erp.constants.filepath.ShopPathConstants;
import com.hsh.erp.domain.common.Result;
import com.hsh.erp.domain.dto.ProductDTO;
import com.hsh.erp.domain.file.FileParams;
import com.hsh.erp.domain.vo.OrderVO;
import com.hsh.erp.enums.ExpressEnum;
import com.hsh.erp.enums.FileEnum;
import com.hsh.erp.enums.ShopEnum;
import com.hsh.erp.init.DataInitializer;
import com.hsh.erp.mapper.OrderMapper;
import com.hsh.erp.service.FileService;
import com.hsh.erp.service.OrderService;
import com.hsh.erp.service.ProductService;
import com.hsh.erp.utils.DataUtils;
import com.hsh.erp.utils.MyBeanUtils;
import com.hsh.erp.utils.POIExcelUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author USER
 * @description 文件处理的service 京东，美团，拼多多，线下等平台的文件 导入，导出 和数据处理
 * @createDate 2024-05-30 15:59:00
 */
@Service
@Slf4j
public class FileServiceImpl implements FileService {
    @Resource
    private OrderMapper orderMapper;
    @Resource
    private ProductService productService;
    @Resource
    private OrderService orderService;


    /**
     * 读取并解析根据店铺导出文件
     * 写入快递导入文件
     *
     * @param shop
     * @param express
     * @param dateStr
     * @param password
     * @return
     */
    @Override
    public Result writeExpressImport(String shop, String express, String dateStr, String password) {
        // 平台的文件路径
        String shopPathPer;

        // 读取文件的参数
        FileParams readParams;
        switch (shop) {
            case "jd":
                readParams = new FileParams(FileEnum.JD_EXPORT, dateStr, password);
                shopPathPer = ShopPathConstants.JD_PATH_PRE;
                break;
            case "mt":
                readParams = new FileParams(FileEnum.MT_EXPORT, dateStr, password);
                shopPathPer = ShopPathConstants.MT_PATH_PRE;
                break;
            default:
                return Result.fail(String.format(ResultConstants.SHOP_IS_NOT_EXIST, shop));
        }

        // 写入文件的参数
        FileParams writeParams;
        switch (express) {
            case "ydgj":
                writeParams = new FileParams(shopPathPer + ExpressPathConstants.YDGJ_IMPORT_FOLDER, FileEnum.YDGJ_EXPRESS_EXPORT, dateStr, password);
                break;
            default:
                String expressName = ExpressEnum.getNameBySimple(express);
                String shopName = ShopEnum.getNameBySimple(shop);
                String format = String.format(ResultConstants.EXPRESS_IS_NOT_EXIST, shopName, express);
                return Result.fail(format);
        }

        return writeExpressImport(readParams, writeParams);
    }

    /**
     * 导入发货文件,
     * 导入订单
     *
     * @param shop
     * @param express
     * @param dateStr
     * @return
     */
    @Override
    public Result importOrder(String shop, String express, String dateStr) {
        // 读取文件的参数
        FileParams readParams;

        // 根据店铺获取店铺的文件前缀
        String shopPathPer;
        switch (shop) {
            case "jd":
                shopPathPer = ShopPathConstants.JD_PATH_PRE;
                break;
            case "mt":
                shopPathPer = ShopPathConstants.MT_PATH_PRE;
                break;
            case "pdd":
                shopPathPer = ShopPathConstants.PDD_PATH_PRE;
                break;
            case "xx":
                shopPathPer = ShopPathConstants.XX_PATH_PRE;
                break;
            default:
                return Result.fail(String.format(ResultConstants.SHOP_IS_NOT_EXIST, shop));
        }


        switch (express) {
            case "ydgj":
                readParams = new FileParams(shopPathPer + ExpressPathConstants.YDGJ_EXPORT_FOLDER, FileEnum.YDGJ_EXPRESS_EXPORT, dateStr);
                break;
            default:
                String expressName = ExpressEnum.getNameBySimple(express);
                String shopName = ShopEnum.getNameBySimple(shop);
                String format = String.format(ResultConstants.EXPRESS_IS_NOT_EXIST, shopName, express);
                return Result.fail(format);
        }

        List<OrderVO> orderVOList = MyBeanUtils.copyList(POIExcelUtils.reads(readParams), OrderVO.class);

        // 解析订单，填充商品信息店铺信息，快递信息
        orderVOList = analyzeOrder(shop, express, orderVOList);
        return orderService.addList(orderVOList);
    }

    /**
     * 解析订单，填充商品信息店铺信息，快递信息
     *
     * @param shop
     * @param express
     * @param orderVOList
     */
    private List<OrderVO> analyzeOrder(String shop, String express, List<OrderVO> orderVOList) {

        List<OrderVO> splitOrderList = new ArrayList<>();
        // 多商品订单拆分订单
        for (OrderVO orderVO : orderVOList) {
            List<ProductDTO> productDTOList = getProductDTOListByStr(orderVO.getProductInfo());
            orderVO.setProductDTOList(productDTOList);
            for (ProductDTO productDTO : productDTOList) {
                OrderVO splitOrderVO = MyBeanUtils.copy(orderVO, OrderVO.class);
                splitOrderVO.setProductId(productDTO.getId())
                        .setPlanNum(productDTO.getNum())
                        .setActualNum(productDTO.getNum());
                splitOrderList.add(splitOrderVO);
            }
        }
        // 设置订单总重量
        setOrderVOWeight(splitOrderList);
        // 设置 店铺，快递，仓库
        splitOrderList.stream().forEach(v -> v.setShopId(DataInitializer.getSimpleShopMap().get(shop))
                .setExpressId(DataInitializer.getSimpleExpressMap().get(express))
                .setRepositoryId(DataInitializer.getDefaultRepositoryId()));

        return splitOrderList;

    }


    /**
     * 根据 商品信息 获取productList
     * 只要 str 符合格式，解析后返回
     * 任意字符 * 数字 （任意字符）
     * 不论商品是否存在
     *
     * @param productStr 任意字符 * 数字 （任意字符）
     * @return 返回商品集合
     */
    @Override
    public List<ProductDTO> getProductDTOListByStr(String productStr) {
        List<ProductDTO> productDTOList = new ArrayList<>();

        // 要解析的字符串为空
        if (Strings.isBlank(productStr)) {
            return productDTOList;
        }

        // 分别以 name 和 simpleName作为键，组装一个map
        List<ProductDTO> allProductDTOList = MyBeanUtils.copyList(productService.getAll().getData(), ProductDTO.class);
        Map<String, ProductDTO> nameProductMap = allProductDTOList.stream()
                .filter(v -> Strings.isNotBlank(v.getName()))
                .collect(Collectors.toMap(ProductDTO::getName, Function.identity()));

        Map<String, ProductDTO> simpleNameProductMap = allProductDTOList.stream()
                .filter(v -> Strings.isNotBlank(v.getSimpleName()))
                .collect(Collectors.toMap(ProductDTO::getSimpleName, Function.identity()));

        // 分割成多条商品信息，并遍历分析这些商品信息
        for (String productInfo : productStr.split(RegexConstants.SEPARATOR_REGEX)) {
            //数据初步处理
            productInfo = productInfo.replaceAll(RegexConstants.MULTIPLE_ZERO_REGEX, "")//去掉*0
                    .replaceAll(RegexConstants.MORE_MULTIPLE_SIGN_REGEX, RegexConstants.ONE_MULTIPLE_SIGN_REGEX); //多个* 换成 单个*
            // 不符合条件，格式不是：任意字符 * 数字 （任意字符）
            if (!productInfo.matches(RegexConstants.NOTE_REGEX)) {
                continue;
            }
            // 符合条件，只保留前面的：任意字符 * 数字
            Matcher matcher = Pattern.compile(RegexConstants.GOODS_INFO_REGEX).matcher(productInfo);
            if (matcher.find()) {
                productInfo = matcher.group();
            }

            // 解析商品信息
            String[] split = productInfo.split(RegexConstants.ONE_MULTIPLE_SIGN_REGEX);
            String name = split[0];
            Integer num = Integer.valueOf(split[1]);
            // 通过商品简称查找
            ProductDTO productDTO = simpleNameProductMap.get(name);
            // 通过商品简称查找失败，通过商品名称查找
            if (productDTO == null) {
                productDTO = nameProductMap.get(name);
            }
            // 均找不到，名称设置为 未知商品
            if (productDTO == null) {
                productDTO = new ProductDTO().setName(ProductConstants.UNKNOWN_PRODUCT).setWeightGram(0);
            }

            // 填充数据
            productDTO.setNum(num);
            productDTO.setWeightGram(productDTO.getWeightGram() * num);
            productDTOList.add(productDTO);

        }
        return productDTOList;
    }


    //============================================私有方法开始============================================


    /**
     * 读取 店铺导出文件 并 写入 店铺导入文件
     * 名字中时间格式精确到毫秒
     *
     * @param readParams
     * @return
     */
    private Result writeExpressImport(FileParams readParams, FileParams writeParams) {
        //读取指定文件，list无需指定实体类
        List shopExportList = POIExcelUtils.reads(readParams);
        // 处理读取到的订单，将 店铺导出的订单 转为 快递导入的订单
        List<OrderVO> orderVOList = ShopExportToExpressImport(MyBeanUtils.copyList(shopExportList, OrderVO.class));
        // 将处理好的订单，转为和writeParams中相匹配的实体类并写入到指定文件
        List expressImportList = MyBeanUtils.copyList(orderVOList, writeParams.getClazz());
        int writeNum = POIExcelUtils.write(writeParams.getPathName(), expressImportList, writeParams.getClazz());
        // 拼接返回的消息
        return Result.success(String.format(writeParams.getResultMsg(), writeNum));
    }


    /**
     * 店铺导出文件 -> List<OrderVO>
     * 根据备注，商品名称，数量
     * 设置每条订单的信息
     * 商品名称，数量，重量
     *
     * @param orderVOList
     * @return orderVOList
     */
    private List<OrderVO> ShopExportToExpressImport(List<OrderVO> orderVOList) {
        // 去除赠品（带 '-'的）和无需发货的（只保留待出库的）
        orderVOList = orderVOList.stream().filter(v -> DataUtils.isNotInclude(ProductConstants.GIVEAWAYS, v.getProductName())).filter(v -> DataUtils.isInclude(ProductConstants.SHIPPING_STATUS, v.getOrderState())).collect(Collectors.toList());
        // 给每一个订单(excel中每一行)设置商品信息列表
        setProductList(orderVOList);
        // 合并订单
        orderVOList = mergeOrder(orderVOList);
        //设置重量
        setOrderVOWeight(orderVOList);
        //拼接商品信息
        setProductInfo(orderVOList);
        // 移除库存不足的订单（一单多种商品时，某一种商品库存够就行）
        orderVOList = removeOutOfStock(orderVOList);

        return orderVOList;
    }

    /**
     * 店铺导出文件 -> List<OrderVO>
     * 给每一个订单(excel中每一行)设置商品信息列表
     * 优先识别备注，备注识别失败用skuNo
     *
     * @param orderVOList
     */
    private void setProductList(List<OrderVO> orderVOList) {
        // 获取商品信息和sku商品关系表信息
        List<String> skuList = new ArrayList<>(orderVOList.stream().map(OrderVO::getSkuNo).collect(Collectors.toList()));
        log.info("setProductList--订单列表中skuList:{}", skuList);
        List<ProductDTO> productDTOList = productService.getProductListBySkuList(skuList);
        log.info("setProductList--skuList对应的productDTOList:{}", productDTOList);

        for (OrderVO orderVO : orderVOList) {
            //先解析备注
            List<ProductDTO> productDTOByOrder = getProductDTOListByStr(orderVO.getNote());
            if (!productDTOByOrder.isEmpty()) {
                orderVO.setProductDTOList(productDTOByOrder);
                continue;
            }

            // 解析skuNo(备注无法解析)
            orderVO.setProductDTOList(getProductDTOListBySkuNo(orderVO, productDTOList));
        }
        log.info("setProductList--设置商品信息后的店铺导出数据：{}", orderVOList);
    }


    /**
     * 店铺导出文件 -> List<OrderVO>
     * 根据skuNo，完善订单中的商品列表（商品，数量，重量）
     *
     * @param orderVO        订单
     * @param productDTOList 符合条件的商品列表
     * @return orderVOProductDTOList 订单的商品列表
     */
    private List<ProductDTO> getProductDTOListBySkuNo(OrderVO orderVO, List<ProductDTO> productDTOList) {
        List<ProductDTO> orderVOProductDTOList = new ArrayList<>();
        String skuNo = orderVO.getSkuNo();
        Integer num = orderVO.getPlanNum();
        if (productDTOList.isEmpty() || Strings.isBlank(skuNo) || num == null) {
            return orderVOProductDTOList;
        }
        //根据sku获取商品，如果找不到，则显示 未知商品*1
        for (ProductDTO productDTO : productDTOList) {
            if (skuNo.equals(productDTO.getSkuNo())) {
                orderVOProductDTOList.add(productDTO);
            } else {
                orderVOProductDTOList.add(new ProductDTO().setName(ProductConstants.UNKNOWN_PRODUCT).setNum(num));
            }
        }
        orderVOProductDTOList = productDTOList.stream().filter(p -> skuNo.equals(p.getSkuNo())).collect(Collectors.toList());
        for (ProductDTO productDTO : orderVOProductDTOList) {
            productDTO.setWeightGram(productDTO.getWeightGram() * num);
            productDTO.setNum(productDTO.getNum() * num);
        }

        return orderVOProductDTOList;
    }

    /**
     * 店铺导出文件 -> List<OrderVO>
     * 根据收件人+收件地址 合并订单
     *
     * @param orderVOList
     */
    private List<OrderVO> mergeOrder(List<OrderVO> orderVOList) {
        Map<String, OrderVO> nameAddressOrderVOMap = new HashMap<>();
        // 如果当前没有订单号和收件人信息，先补全信息
        for (OrderVO orderVO : orderVOList) {
            if (Strings.isBlank(orderVO.getNo()) && orderVOList.indexOf(orderVO) > 0) {
                OrderVO lastOrderVO = orderVOList.get(orderVOList.indexOf(orderVO) - 1);
                //该行没有订单号等信息时，从上一行获取
                orderVO.setNo(lastOrderVO.getNo()).setCustomerName(lastOrderVO.getCustomerName()).setCustomerAddress(lastOrderVO.getCustomerAddress());
            }
        }
        // 根据收件人+收件地址，合并订单
        for (OrderVO orderVO : orderVOList) {
            String nameAddress = orderVO.getCustomerName() + orderVO.getCustomerAddress();
            OrderVO oldOrderVO = nameAddressOrderVOMap.get(nameAddress);
            // 该 收件人+收件地址 未出现过，无需合并产品信息
            if (oldOrderVO == null) {
                nameAddressOrderVOMap.put(nameAddress, orderVO);
                continue;
            }
            // 合并两个订单的产品信息
            nameAddressOrderVOMap.put(nameAddress, mergeProduct(oldOrderVO, orderVO));
        }
        log.info("mergeOrder--合并订单前记录条数:{},数据为：{}", orderVOList.size(), orderVOList);
        orderVOList = new ArrayList<>(nameAddressOrderVOMap.values());
        log.info("mergeOrder--合并订单前记录条数:{},数据为：{}", orderVOList.size(), orderVOList);
        return orderVOList;
    }

    /**
     * 店铺导出文件 -> List<OrderVO>
     * 合并两个订单的产品信息
     *
     * @param mainOrderVO
     * @param secondaryOrderVO
     * @return
     */
    private OrderVO mergeProduct(OrderVO mainOrderVO, OrderVO secondaryOrderVO) {
        if (mainOrderVO == null || secondaryOrderVO == null) {
            return mainOrderVO;
        }
        // 商品拼接
        List<ProductDTO> secondaryList = secondaryOrderVO.getProductDTOList();
        List<ProductDTO> mainList = mainOrderVO.getProductDTOList();
        boolean mainEmpty = mainList.isEmpty();
        boolean secondaryEmpty = secondaryList.isEmpty();

        if (!mainEmpty && !secondaryEmpty) {//两个均不为空时
            mainList.addAll(secondaryList);
            mainOrderVO.setProductDTOList(mainList);
        } else if (mainEmpty && !secondaryEmpty) {//主要订单的商品集合空，次要不空时
            mainOrderVO.setProductDTOList(secondaryList);
        }//其他情况无需处理

        // 订单号拼接
        String secondaryOrderNo = secondaryOrderVO.getNo();
        String mainOrderNo = mainOrderVO.getNo();
        //次要订单号不存在或与主要订单号重复，无需拼接
        if (Strings.isBlank(secondaryOrderNo) || secondaryOrderNo.equals(mainOrderNo)) {
            return mainOrderVO;
        }
        String mergeOrderNo = mainOrderVO.getMergeNo();
        if (Strings.isBlank(mergeOrderNo)) {
            // 之前未合并过，将两个订单号拼接
            mainOrderVO.setMergeNo(mainOrderNo + SpecialSymbolConstants.COMMA + secondaryOrderNo);
        } else {
            // 直接合并过，将次要订单号拼接到主要订单的合并订单号中
            mainOrderVO.setMergeNo(mergeOrderNo + SpecialSymbolConstants.COMMA + secondaryOrderNo);
        }
        return mainOrderVO;
    }

    /**
     * 根据每个 orderVO 的 productList
     * 店铺导出文件 -> List<OrderVO>
     * 快递导出文件 -> List<OrderVO>
     * 为订单设置重量
     *
     * @param orderVOList
     */
    private void setOrderVOWeight(List<OrderVO> orderVOList) {
        // 遍历所有订单
        for (OrderVO orderVO : orderVOList) {
            List<ProductDTO> productDTOList = orderVO.getProductDTOList();
            int allWeightGram = 0;
            for (ProductDTO productDTO : productDTOList) {
                int productDTOWeight = productDTO.getWeightGram() == null ? 0 : productDTO.getWeightGram();
                allWeightGram += productDTOWeight;
            }
            orderVO.setWeightGram(allWeightGram).init();
        }
    }

    /**
     * 店铺导出文件 -> 快递导入文件
     * 获取商品信息文本
     * 商品1*数量1；商品2*数量2...
     *
     * @param orderVOList
     */
    private void setProductInfo(List<OrderVO> orderVOList) {
        for (OrderVO orderVO : orderVOList) {
            List<ProductDTO> productDTOList = orderVO.getProductDTOList();
            if (productDTOList == null) {
                continue;
            }
            StringBuffer stringBuffer = new StringBuffer();
            for (ProductDTO productDTO : productDTOList) {
                String name = productDTO.getSimpleName() == null ? productDTO.getName() : productDTO.getSimpleName();
                Integer num = productDTO.getNum();
                if (Strings.isBlank(name) || num == null) {
                    continue;
                }
                if (stringBuffer.length() > 0) {
                    stringBuffer.append(";");
                }
                stringBuffer.append(name).append("*").append(num);
            }
            orderVO.setProductInfo(stringBuffer.toString());
        }
    }

    /**
     * 店铺导出文件 -> 快递导入文件
     * 移除库存不足的订单（一单多种商品时，某一种商品库存够就行）
     *
     * @param orderVOList
     * @return
     */
    private List<OrderVO> removeOutOfStock(List<OrderVO> orderVOList) {
        // 所有未删除的商品信息
        List<ProductDTO> productDTOList = productService.getAll().getData();
        System.out.println(productDTOList);
        // 转成 map<商品名称，数量>
        Map<String, Integer> map = productDTOList.stream().collect(Collectors.toMap(ProductDTO::getName, v -> v.getNum() == null ? 0 : v.getNum()));
        List<OrderVO> resultShopExportList = new ArrayList<>();
        for (OrderVO orderVO : orderVOList) {
            // 订单为空
            if (orderVO == null) {
                continue;
            }
            // 该订单是否所有商品均为缺货，默认均缺货
            boolean allOutStock = true;
            for (ProductDTO productDTO : orderVO.getProductDTOList()) {
                String name = productDTO.getName();
                Integer orderNum = productDTO.getNum();
                // 剩余库存
                Integer stockNum = map.get(name);
                // 订单中还没找到有库存的商品 并且 当前商品库存不足
                if (allOutStock && (stockNum == null || stockNum <= 0)) {
                    continue;
                }
                // 更新该商品库存
                map.put(name, stockNum - orderNum);
                allOutStock = false;
            }
            // 订单中至少有一个商品有库存时，保留该订单，其余（订单中商品都缺货）舍弃
            if (!allOutStock) {
                resultShopExportList.add(orderVO);
            }
        }
        log.info("去除库存不足记录之前的记录数：{},数据为；{}", orderVOList.size(), orderVOList);
        log.info("去除库存不足记录之后的记录数：{},数据为；{}", resultShopExportList.size(), resultShopExportList);
        return resultShopExportList;
    }

    //============================================私有方法结束============================================
}




